package cc.rengu.redp.flowable.service.impl;

import cc.rengu.redp.common.domain.QueryRequest;
import cc.rengu.redp.common.exception.RedpException;
import cc.rengu.redp.common.utils.DateUtil;
import cc.rengu.redp.common.utils.RedpUtil;
import cc.rengu.redp.flowable.domain.*;
import cc.rengu.redp.flowable.service.FlowDesignService;
import cc.rengu.redp.flowable.service.ProcessDesignService;
import cc.rengu.redp.flowable.service.SysWorkFlowModelService;
import cc.rengu.redp.flowable.service.SysWorkFlowService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.RandomStringGenerator;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;

import static org.apache.commons.text.CharacterPredicates.DIGITS;
import static org.apache.commons.text.CharacterPredicates.LETTERS;

@Slf4j
@Service("processDesignService")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class ProcessDesignServiceImpl implements ProcessDesignService {

    @Autowired
    private FlowDesignService flowDesignService;
    @Autowired
    private SysWorkFlowModelService sysWorkFlowModelService;
    @Autowired
    private SysWorkFlowService sysWorkFlowService;

    @Override
    public IPage<ProcessDesignVo> procDesignList(ProcessDesignVo processDesignVo, QueryRequest queryRequest) throws Exception {
        FlowDesign flowDesignQuery = new FlowDesign();
        IPage<FlowDesign> dataPage = flowDesignService.flowDesignQuery(flowDesignQuery, queryRequest);
        if (dataPage.getRecords() == null) {
            throw new RedpException("查询异常");
        }

        Page<ProcessDesignVo> page = new Page<>();
        List<ProcessDesignVo> processDesignVos = new ArrayList<>();
        page.setRecords(processDesignVos);
        page.setTotal(dataPage.getTotal());

        for (FlowDesign fd : dataPage.getRecords()) {
            ProcessDesignVo pdv = new ProcessDesignVo();
            pdv.setId(fd.getModelId().toString());
            pdv.setName(fd.getModelName());
            pdv.setDefinitionKey(fd.getModelKey());
            pdv.setCreated(fd.getCreatedDate());
            pdv.setLastUpdated(fd.getModifyTime());
            //流程状态查询
            ProcessDefinition pd = sysWorkFlowService.getProcDefByProcDefKey(fd.getModelKey());
            if (pd == null) {
                //未发布
                pdv.setVersion("?");
                pdv.setDraftStatus("未发布");
                pdv.setDeployStatus("未发布");
            } else {
                pdv.setVersion(String.valueOf(pd.getVersion()));
                Deployment deployment = sysWorkFlowService.getDeploymentById(pd.getDeploymentId());
                pdv.setDeployTime(DateUtil.dateToLocalDateTime(deployment.getDeploymentTime()));
                LocalDateTime deployLdt = deployment.getDeploymentTime()
                        .toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime();
                LocalDateTime updatedLdt = fd.getModifyTime();
                if (updatedLdt.isAfter(deployLdt)) {
                    //未发布
                    pdv.setDraftStatus("未发布");
                } else {
                    //已发布
                    pdv.setDraftStatus("已发布");
                }
                if (pd.isSuspended()) {
                    pdv.setDeployStatus("已挂起");
                } else {
                    pdv.setDeployStatus("活动中");
                }
            }
            processDesignVos.add(pdv);
        }

        return page;
    }

    @Override
    public void deleteProcDesign(String modelId) throws RedpException {
        FlowDesign flowDesign = flowDesignService.getById(modelId);
        if (flowDesign == null) {
            throw new RedpException("模型不存在");
        }

        ProcessDefinition pd = sysWorkFlowService.getProcDefByProcDefKey(flowDesign.getModelKey());
        if (pd != null) {
            sysWorkFlowService.deleteDeployment(flowDesign.getModelKey(), null, false);
        }

        flowDesignService.removeById(modelId);
    }

    @Override
    public Map<String, Object> listProcessAllNodes(String modelId) throws Exception {
        Map<String, Object> result = new HashMap<>();
        List<CustomNodeVo> list = sysWorkFlowModelService.getCustomNodesByModelId(modelId);
        result.put("rows", list);
        result.put("total", list.size());
        return result;
    }

    @Override
    public void saveFlowDesign(String editorJson) throws RedpException {
        FlowDesignDrawer flowDesignDrawer = JSON.parseObject(editorJson, FlowDesignDrawer.class);
        FlowDesign flowDesign;

        if (StringUtils.isNotBlank(flowDesignDrawer.getAttr().getPersistId())) {
            flowDesign = flowDesignService.getById(Long.valueOf(flowDesignDrawer.getAttr().getPersistId()));
            if (flowDesign == null) {
                throw new RedpException("无效modelId");
            }
        } else {
            flowDesign = new FlowDesign();
        }

        //todo modelKey防重检查

        flowDesign.setModelKey(flowDesignDrawer.getAttr().getId());
        flowDesign.setModelName(flowDesignDrawer.getAttr().getName());
        flowDesign.setDescription(flowDesignDrawer.getAttr().getDescription());
        flowDesign.setModelImage(flowDesignDrawer.getPreviewImage());
        flowDesignDrawer.setPreviewImage(null); //清空图像信息，EditorJson字段不保存，编辑时也不返回，图像信息由设计器在保存时自动生成
        flowDesign.setEditorJson(JSON.toJSONString(flowDesignDrawer));
        flowDesign.setModifyTime(LocalDateTime.now());
        flowDesign.setModifyBy(RedpUtil.getCurrentUser().getUsername());
        if (flowDesign.getModelId() == null) {
            flowDesign.setCreatedDate(LocalDateTime.now());
        }

        flowDesignService.saveOrUpdate(flowDesign);
    }

    @Override
    public String getFlowDesignDesign(String modelId) {
        FlowDesign flowDesign = flowDesignService.getById(Long.valueOf(modelId));
        if (flowDesign != null) {
            FlowDesignDrawer flowDesignDrawer = JSON.parseObject(flowDesign.getEditorJson(), FlowDesignDrawer.class);
            flowDesignDrawer.getAttr().setPersistId(flowDesign.getModelId().toString());
            return JSON.toJSONString(flowDesignDrawer);
        } else {
            return null;
        }
    }

    @Override
    public void copyFlowDesign(String modelId) {
        FlowDesign flowDesign = flowDesignService.getById(Long.valueOf(modelId));
        if (flowDesign == null) {
            log.error("无法获取模型配置 {}", modelId);
            return;
        }

        FlowDesignDrawer flowDesignDrawer = JSON.parseObject(flowDesign.getEditorJson(), FlowDesignDrawer.class);
        //清空id
        flowDesign.setModelId(null);
        flowDesign.setModelKey(checkIfDuplicated(null));
        flowDesignDrawer.getAttr().setId(flowDesign.getModelKey());  //同步修改绘图内的id
        flowDesign.setCreatedDate(LocalDateTime.now());
        flowDesign.setModifyTime(LocalDateTime.now());
        flowDesign.setModifyBy(RedpUtil.getCurrentUser().getUsername());
        flowDesign.setModelName(flowDesign.getModelName() + "_副本");//同步修改绘图内的名字
        flowDesignDrawer.getAttr().setName(flowDesign.getModelName());
        flowDesign.setEditorJson(JSON.toJSONString(flowDesignDrawer));
        flowDesignService.save(flowDesign);
    }

    @Override
    public void deployProcDesign(String modelId) throws Exception {
        sysWorkFlowService.deployFromModeler(modelId);
    }

    @Override
    public String getFlowImage(String modelId) throws RedpException {
        return sysWorkFlowModelService.getImageFromModel(modelId);
    }

    @Override
    public void suspendDeployment(String modelId, Boolean allCurrentActiveInstances) throws RedpException {
        FlowDesign flowDesign = flowDesignService.getById(Long.valueOf(modelId));
        if (flowDesign != null) {
            sysWorkFlowService.suspendDeployment(flowDesign.getModelKey(), allCurrentActiveInstances, null);
        } else {
            throw new RedpException("无法获取模型配置");
        }
    }

    @Override
    public void activeDeployment(String modelId, Boolean allCurrentActiveInstances) throws RedpException {
        FlowDesign flowDesign = flowDesignService.getById(Long.valueOf(modelId));
        if (flowDesign != null) {
            sysWorkFlowService.activeDeployment(flowDesign.getModelKey(), allCurrentActiveInstances, null);
        } else {
            throw new RedpException("无法获取模型配置");
        }
    }

    private String checkIfDuplicated(String modelKey) {
        if (modelKey == null || flowDesignService.getOneByModelKey(modelKey) != null) {
            RandomStringGenerator generator = new RandomStringGenerator.Builder()
                    .withinRange('0', 'f').filteredBy(LETTERS, DIGITS).build();
            return "flow-" + generator.generate(32).toLowerCase(Locale.ROOT);
        } else {
            return modelKey;
        }
    }
}
